package main

import (
	"fmt"
	"math"
	"math/rand"
	"strconv"
	"sync"
	"time"

	cmap "github.com/orcaman/concurrent-map"
)

const (
	ARC      = 6371000.0 //6378137.0
	rad      = math.Pi / 180.0
	posChCap = 8
	minSpeed = 6
	maxSpeed = 25
)

type location struct {
	Latitude  float64
	Longitude float64
}

type carPosInfo struct {
	CarID string
	location
}

func NewLocation(initial location) location {
	lat1 := initial.Latitude   // 起始点纬度
	long1 := initial.Longitude // 起始点经度

	distanceM := float64(minSpeed+rand.Intn(maxSpeed-minSpeed)) / 3.6 // 随机移动距离
	radian := float64(math.Pi/180) * float64(rand.Intn(360))          // 方位角转弧度

	lat2 := lat1 + (distanceM * math.Cos(radian) / (ARC * rad))                    // 终点纬度
	long2 := long1 + (distanceM * math.Sin(radian) / (ARC * math.Cos(lat1) * rad)) // 终点经度

	return location{
		Latitude:  lat2,
		Longitude: long2,
	}
}

func getDistance(old, new location) float64 {
	lat1 := old.Latitude * rad
	long1 := old.Longitude * rad
	lat2 := new.Latitude * rad
	long2 := new.Longitude * rad
	theta := long2 - long1
	dist := math.Acos(math.Sin(lat1)*math.Sin(lat2) + math.Cos(lat1)*math.Cos(lat2)*math.Cos(theta))
	return dist * ARC / 1000
}

func SimulateCarPos(initial carPosInfo, closeCh chan struct{}, receiveCh chan carPosInfo) {
	var position location = initial.location
	for {
		select {
		case <-time.After(100 * time.Millisecond):
		case <-closeCh:
			return
		}
		// 生成模拟数据
		position = NewLocation(position)
		fmt.Printf("ID:%s\tPos:%v\n", initial.CarID, position)
		// 传到模拟RabbitMQ的channel中
		receiveCh <- carPosInfo{
			CarID:    initial.CarID,
			location: position,
		}
	}
}

func SimulatePosition(wg *sync.WaitGroup, id string, initial location, carChans cmap.ConcurrentMap, receiveCh chan carPosInfo) {
	defer wg.Done()
	ch := make(chan struct{})
	// carChans[id] = ch
	carChans.Set(id, &ch)
	SimulateCarPos(carPosInfo{
		CarID:    id,
		location: initial,
	}, ch, receiveCh)
}

func EndSimulatePosition(id string, carChans cmap.ConcurrentMap) {
	// ch, ok := carChans[id]
	// if !ok {
	// 	return
	// }
	tmp, ok := carChans.Get(id)
	if !ok {
		return
	}
	ch, ok := tmp.(*chan struct{})
	if !ok {
		return
	}
	close(*ch)
	// delete(carChans, id)
	carChans.Remove(id)

	fmt.Println("close", id, "ch")
}

func GetChanValues(receiveCh chan carPosInfo) {
	for range receiveCh {
		// fmt.Printf("ID:%d\t位置:%v\n", value.CarID, value.location)
	}
}

func main() {
	l := location{
		Latitude:  30,
		Longitude: 120,
	}

	receiveCh := make(chan carPosInfo, 8)
	// carChans := make(map[string]chan struct{}, 4)
	carChans := cmap.New()

	var wg sync.WaitGroup
	for i := 0; i < 5; i++ {
		wg.Add(1)
		id := strconv.Itoa(i)
		go SimulatePosition(&wg, id, l, carChans, receiveCh)
	}

	go GetChanValues(receiveCh)

	time.Sleep(3 * time.Second)
	fmt.Printf("%d\n", carChans.Count())

	for i := 0; i < 5; i++ {
		id := strconv.Itoa(i)
		EndSimulatePosition(id, carChans)
	}

	wg.Wait()

	fmt.Printf("%t\n", carChans.IsEmpty())
}
